#!/bin/sh
# Part of spindle http://asbradbury.org/projects/spindle
#
# See LICENSE file for copyright and license details

set -e

. ./config

run_qemu() {
  rm fifo.out fifo.in || true
  mkfifo fifo.out fifo.in
  qemu-system-arm -M versatilepb -cpu arm1136-r2 -m 256 -nographic -no-reboot \
        -kernel zImage -hda qemu_rootfs.sqf -drive file=$1,index=1,media=disk,cache=unsafe \
        -drive file=wheezy_apt_cache.$IMGFORMAT,index=2,media=disk,cache=unsafe \
        -append "root=/dev/sda rw init=/sbin/init.sh panic=1 PATH=/bin:/sbin console=ttyAMA0 HOST=armv6l"\
        -net nic,model=rtl8139 -net user -redir tcp:22000::22 -daemonize -serial pipe:fifo \
        -pidfile qemu.pid
  sleep 10
}

ssh_in_to_qemu() {
  ssh -i qemu_arm_key -p 22000 -lroot localhost "$@"
}

scp_in_to_qemu() {
  scp -i qemu_arm_key -P 22000 "$1" root@localhost:"$2"
}

scp_r_from_qemu() {
  scp -r -i qemu_arm_key -P 22000 root@localhost:"$1" "$2"
}

onvm_chroot() {
  ssh_in_to_qemu /usr/sbin/chroot /mnt "$@"
}

shutdown_qemu() {
  ssh_in_to_qemu "sync && umount -a" || true
  echo "exit" > fifo.in
  sleep 5
  if [ -e qemu.pid ]; then
    QEMU_PID=$(cat qemu.pid)
    while [ -n "$QEMU_PID" ]; do 
      set +e
      kill -0 $QEMU_PID 2>/dev/null
      if [ $? -eq 0 ]; then
        printf "Qemu pid %s not finished yet. Waiting\n" "$QEMU_PID"
        sleep 1
      else
        QEMU_PID=""
      fi
      set -e
    done
  fi
  rm fifo.in
  rm fifo.out
#  sleep 15
}

attach_image_to_nbd() {
  # use -v as we seem to have problems otherwise...
  sudo qemu-nbd -v -c $2 $1 &
  sleep 5
}

detach_image_from_nbd() {
  sudo qemu-nbd -d $1
}

inspect_image() {
  cd work
  qemu-img create -f $IMGFORMAT -b ../$1 temp.$IMGFORMAT
  # Sigh http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=390433
  trap '[ -p fifo.in ] && shutdown_qemu' EXIT
  trap '[ -p fifo.in ] && shutdown_qemu; trap - INT; kill -INT $$' INT
  run_qemu temp.$IMGFORMAT
  ssh_in_to_qemu # -t /usr/sbin/chroot /mnt bash -l
  shutdown_qemu
  cd $OLDPWD
}

branch_image() {
  qemu-img create -f $IMGFORMAT -b $1 $2
}

write_image() {
  attach_image_to_nbd $1 /dev/nbd0 &&
  sudo dd if=/dev/nbd0 of=$2 bs=4M &&
  detach_image_from_nbd /dev/nbd0
}

convert_image() {
  qemu-img convert -f $IMGFORMAT -O raw $1 $2
}

export_image_for_release() {
  cd work
  qemu-img create -f $IMGFORMAT -b ../$1 temp.$IMGFORMAT
  run_qemu temp.$IMGFORMAT
  # Change mirror in sources.list to our preferred one. This will go horribly 
  # wrong if there is more than one entry in sources.list
  onvm_chroot sh -l -ex <<\EOF
if grep armhf /etc/rpi-issue; then
  sed -i /etc/apt/sources.list -e "s|^deb [^ ]*|deb http://mirrordirector.raspbian.org/raspbian/|"
else
  sed -i /etc/apt/sources.list -e "s|^deb [^ ]*|deb http://http.debian.net/debian|"
fi
#cp /usr/share/doc/raspi-config/sample_profile_d.sh /etc/profile.d/raspi-config.sh
mv /etc/torberry.sh /etc/profile.d/torberry.sh
apt-get update
apt-get install -y raspi-copies-and-fills
#/etc/init.d/fake-hwclock stop # save current time
#rm /usr/bin/raspi-config
#cd /usr/bin
#wget https://raw.github.com/asb/raspi-config/master/raspi-config
#chmod +x raspi-config
EOF
  shutdown_qemu
  attach_image_to_nbd temp.$IMGFORMAT /dev/nbd0
  sudo zerofree -v /dev/nbd0p2
  mkdir -p rootfs boot
  sudo mount $BOOT_DEV boot
  sudo mount $ROOT_DEV rootfs
  sudo cp rootfs/etc/rpi-issue boot/issue.txt
  sudo umount $BOOT_DEV
  sudo umount $ROOT_DEV
  echo "we are previous detach"
  detach_image_from_nbd /dev/nbd0
  echo "we are previous convert"
  convert_image temp.$IMGFORMAT ../$2
  echo "we are post convert"
}

disable_starting_services() {
  ssh_in_to_qemu /usr/sbin/chroot /mnt sh -ex - <<EOF
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
echo "Debugging printf"
printf '#!/bin/sh\nexit 101\n' > /usr/sbin/policy-rc.d
chmod 755 /usr/sbin/policy-rc.d
EOF
}

allow_starting_services() {
  ssh_in_to_qemu /usr/sbin/chroot /mnt sh -ex - <<EOF
rm /usr/sbin/policy-rc.d
EOF
}

save_space_using_hardlink() {
  onvm_chroot sh -l -e - <<\EOF
hardlink -t /usr/share/doc
EOF
}

mount_apt_cache() {
  ssh_in_to_qemu sh -e - <<EOF
  mount -o noatime /dev/sdc /mnt/var/cache/apt
EOF
}

download_if_necessary() {
  [ -f $(basename "$1") ] || wget "$1"
}

get_git_hash() {
  git log --pretty=format:'%h' -n 1
}

update_issue() {
  GIT_HASH=$(get_git_hash) &&
  CUR_DATE=$(date "+%Y-%m-%d")
  onvm_chroot sh -l -e <<EOF
printf "Raspberry Pi reference $CUR_DATE ($TGT_ARCH)\nGenerated using spindle, http://asbradbury.org/projects/spindle/, $GIT_HASH, $CURIMG\n" > /etc/rpi-issue
EOF
}

# Create a list of all installed packages, all manually installed packages and 
# the debconf selections
fingerprint_debian() {
  FNGPRNT_DIR="$CURIMG.fingerprint" &&
  onvm_chroot sh -l -e <<EOF &&
mkdir -p "/tmp/$FNGPRNT_DIR"
cd "/tmp/$FNGPRNT_DIR"
dpkg --get-selections > dpkg_selections
dpkg -l > dpkg_l
apt-mark showauto > apt-mark_showauto
debconf-get-selections > debconf_selections
EOF
  scp_r_from_qemu "/mnt/tmp/$FNGPRNT_DIR" . &&
  cd "$FNGPRNT_DIR" &&
  grep "[[:space:]]install$" dpkg_selections | cut -f1 | sort apt-mark_showauto - | uniq -u > manually_installed_packages &&
  cd "$OLDPWD"
}

finish_image() {
  chmod -w $CURIMG &&
  mkdir -p ../$OUTDIR &&
  mv -f $CURIMG ../$OUTDIR &&
  if [ -d "$CURIMG.fingerprint" ]; then
    rm -rf "../$OUTDIR/$CURIMG.fingerprint" &&
    mv "$CURIMG.fingerprint" ../$OUTDIR
  fi &&
  FINISHED_SUCCESSFULLY=1 &&
  printf "Completed script successfully\n"
}

# Usage: ask_yn default prompt [printf_args...]
# Read a Yes / No user response. If $ASK_YN_USE_DEFAULT is set, assume the 
# default. Return of 0 indicates a yes, and non-zero is no.
ask_yn() {
  OPT="y/n"
  case "$1" in
    y*|Y*) OPT="Y/n"; RET=0;;
    n*|N*) OPT="y/N"; RET=1;;
    *) die "Programmer error: invalid argument to ask_yn"
  esac
  shift
  if [ "$ASK_YN_USE_DEFAULT" ]; then
    return $RET
  else
    PROMPT=$1
    shift
    printf "$PROMPT [$OPT]: " "$@" && read YN
    case "$YN" in
      y*|Y*) return 0;;
      n*|N*) return 1;;
      "") return $RET
    esac
  fi
}

# Usage: read_val var default prompt [printf_args...]
read_val() {
  RV_VAR=$1
  RV_DEFAULT=$2
  RV_PROMPT=$3
  shift 3
  printf "$RV_PROMPT [$RV_DEFAULT]: " "$@" && read RV_VAL
  [ -z "$RV_VAL" ] && RV_VAL=$RV_DEFAULT
  eval "$RV_VAR=\$RV_VAL"
}

die() {
  FMTSTR=$1
  shift
  printf "Died: $FMTSTR\n" "$@" >&2
  exit 1
}

# do a task
# Inspects $DOTASK_MODE to see if the user:
#   e*: wants to have the command echoed
#   q*: wants to be questioned as to whether to run, skip or quit.
# If the task fails it asks the user whether to quit (default: y), or if 
# $DOTASK_QUIT_ON_FAILURE is set then quit without asking the user.
dotask() {
  case "$DOTASK_MODE" in
    e*) printf "Run: '%s'\n" "$*"; false;;
    q*) printf "Run: '%s' [Y/n/q] ? " "$*" && read SHOULD_RUN;
        case "$SHOULD_RUN" in
          n*|N*|s*|S*)  printf "Skip '%s'\n" "$*";;
          q*|Q*)        die "Exit at user request";;
          *)            false;;
        esac;;
    *)  false;;
  esac || "$@" || { RC=$?
    [ -z "$DOTASK_QUIT_ON_FAILURE" ] && printf "'%s' failed (returned $RC) - Quit? [Y/n] : " "$*" && read YN
    case "$YN" in n*|N*) true ;; *) die "Failed while performing task: %s" "$*";; esac;
  }
}


FINISHED_SUCCESSFULLY=0
CLEANED_UP=0
WAS_TRAPPED=0

# Sigh http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=390433
trap 'WAS_TRAPPED=1; universal_cleanup' EXIT
trap 'WAS_TRAPPED=1; universal_cleanup; trap - INT; kill -INT $$' INT

universal_cleanup() {
  set +e
  if [ $CLEANED_UP -ne 1 ]; then
    printf "Initiating cleanup\n"
    trap - INT
    trap - EXIT
    [ -p fifo.in ] && shutdown_qemu
    [ -b "$BOOT_DEV" ] && sudo umount $BOOT_DEV
    [ -b "$ROOT_DEV" ] && sudo umount $ROOT_DEV
    [ -b "$NBD_DEV" ] && sudo umount $NBD_DEV
    [ -b "$NBD_DEV" ] && detach_image_from_nbd $NBD_DEV
  fi
  if [ $WAS_TRAPPED -eq 1 ] && [ $FINISHED_SUCCESSFULLY -eq 0 ]; then
    printf "Did not complete script successfully\n"
  fi
  set -e
}
